package com.corpgovernment.organization.service.budget;

import com.corpgovernment.api.costcenter.model.CostCenter;
import com.corpgovernment.api.costcenter.model.GetProjectInfoRequest;
import com.corpgovernment.api.costcenter.model.OrgInfo;
import com.corpgovernment.api.costcenter.service.ICostCenterService;
import com.corpgovernment.api.organization.bo.budget.*;
import com.corpgovernment.api.organization.bo.budget.model.clickAdd.*;
import com.corpgovernment.api.organization.bo.budget.model.searchBudget.SearchBudgetDetailModel;
import com.corpgovernment.api.organization.bo.budget.model.searchBudget.SearchBudgetModel;
import com.corpgovernment.api.organization.bo.budget.model.searchDepart.DepartModel;
import com.corpgovernment.api.organization.bo.budget.model.searchDepart.SearchDepartModel;
import com.corpgovernment.api.organization.bo.budget.model.seeBudget.SeeBudgetDetailModel;
import com.corpgovernment.api.organization.bo.budget.model.seeBudget.SeeBudgetModel;
import com.corpgovernment.api.organization.bo.budget.model.seeBudget.SeeBudgetSubsidiaryDetailModel;
import com.corpgovernment.api.organization.bo.budget.model.seeBudget.SeeBudgetSubsidiaryModel;
import com.corpgovernment.api.platform.soa.response.GetMbBudgetInfoResponse;
import com.corpgovernment.common.base.AbstractBaseService;
import com.corpgovernment.common.utils.BaseUtils;
import com.corpgovernment.common.utils.BigDecimalUtil;
import com.corpgovernment.common.utils.DateUtil;
import com.corpgovernment.costcenter.entity.db.MbOrgProjectInfo;
import com.corpgovernment.costcenter.service.IProjectInfoService;
import com.corpgovernment.organization.dto.AmountTotalDTO;
import com.corpgovernment.organization.dto.TimeIntervalDTO;
import com.corpgovernment.organization.entity.db.budget.*;
import com.corpgovernment.organization.enumm.budget.BudgetChangeEnum;
import com.corpgovernment.organization.enumm.budget.BudgetScopeEnum;
import com.corpgovernment.organization.enumm.budget.BudgetStatusEnum;
import com.corpgovernment.organization.enumm.budget.BudgetTypeEnum;
import com.corpgovernment.organization.mapper.budget.*;
import com.corpgovernment.organization.model.budget.BudgetDetailFuture;
import com.corpgovernment.organization.model.budget.BudgetInfoList;
import com.corpgovernment.organization.util.BudgetAlarmUtil;
import com.corpgovernment.organization.util.BudgetDateUtil;
import com.corpgovernment.organization.util.PreCheckUtil;
import com.corpgovernment.organization.vo.budget.*;
import com.ctrip.corp.obt.generic.core.context.RequestContext;
import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.generic.utils.JsonUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.ctrip.corp.obt.shard.context.PageContext;
import com.ctrip.corp.obt.shard.sql.mybatis.interceptor.pagination.IPage;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.SetUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import static com.corpgovernment.permission.config.PermissionFilter.DATA_PERMISSION_ORG_ID_LIST;

@Slf4j
@Service
public class SearchBudgetService extends AbstractBaseService {

    @Autowired
    private ICostCenterService costCenterService;
    @Autowired
    private BudgetInfoMapper budgetInfoMapper;
    @Autowired
    private BudgetGroupMapper budgetGroupMapper;
    @Autowired
    private BudgetItemMapper budgetItemMapper;
    @Autowired
    private BudgetNotifyMapper budgetNotifyMapper;
    @Autowired
    private BudgetRecordMapper budgetRecordMapper;
    @Autowired
    private IProjectInfoService iProjectInfoService;
    @Autowired
    @Qualifier(value = "defaultThreadPoolExecutor")
    private ThreadPoolExecutor defaultThreadPoolExecutor;
    /**
     * 查询公司下已配置了预算的部门
     */
    public SearchDepartModel searchDepart(String corpId, String orgName) {
        SearchDepartModel searchDepartModel = new SearchDepartModel();
        List<BudgetGroupEntity> budgetGroups = budgetGroupMapper.selectByCorpIdAndOrgName(corpId, orgName, BudgetScopeEnum.DEP.getType());
        if (CollectionUtils.isEmpty(budgetGroups)) {
            return searchDepartModel;
        }
        Map<String, String> maps = budgetGroups.stream().collect(Collectors.toMap(BudgetGroupEntity::getScopeId, BudgetGroupEntity::getScopeName, (o1, o2) -> o1));
        searchDepartModel.setDepartList(maps.keySet().stream().map(k -> new DepartModel(k, maps.get(k))).collect(Collectors.toList()));
        return searchDepartModel;
    }

    public List<GetMbBudgetInfoResponse> getMbBudgetInfoResponses() {
        List<BudgetInfoEntity> budgetInfoEntities = budgetInfoMapper.getCycleBudgetInfo(DateUtil.getNowDayYMD());
        if (CollectionUtils.isEmpty(budgetInfoEntities)) {
            return Lists.newArrayList();
        }
        List<String> collect = budgetInfoEntities.stream().map(BudgetInfoEntity::getBudgetId).collect(Collectors.toList());
        Map<String, List<BudgetGroupEntity>> groupMap = budgetGroupMapper.selectByBudgetIds(collect).stream().collect(Collectors.groupingBy(BudgetGroupEntity::getBudgetId));
        Map<String, List<BudgetItemEntity>> itemMap = budgetItemMapper.selectByBudgetIds(collect).stream().collect(Collectors.groupingBy(BudgetItemEntity::getBudgetId));
        List<GetMbBudgetInfoResponse> responses = new ArrayList<>();
        for (BudgetInfoEntity budgetInfoEntity : budgetInfoEntities) {
            GetMbBudgetInfoResponse response = new GetMbBudgetInfoResponse();
            response.setId(budgetInfoEntity.getBudgetId());
            response.setCorpId(budgetInfoEntity.getCorpId());
            response.setBudgetType(budgetInfoEntity.getBudgetType());
            response.setScopeIds(
                    groupMap.get(budgetInfoEntity.getBudgetId()).stream().map(BudgetGroupEntity::getScopeId).collect(Collectors.toSet())
            );
            response.setScopeNames(
                    groupMap.get(budgetInfoEntity.getBudgetId()).stream().collect(Collectors.toMap(BudgetGroupEntity::getScopeId, BudgetGroupEntity::getScopeName))
            );


            List<BudgetItemEntity> budgetItemEntities = itemMap.get(budgetInfoEntity.getBudgetId());

            List<GetMbBudgetInfoResponse.BudgetInfo> budgetInfos = new ArrayList<>();
            for (BudgetItemEntity infoEntity : budgetItemEntities) {
                GetMbBudgetInfoResponse.BudgetInfo budgetInfo = new GetMbBudgetInfoResponse.BudgetInfo();
                budgetInfo.setId(infoEntity.getId());
                budgetInfo.setIsAll(infoEntity.getIsAll());
                budgetInfo.setType(infoEntity.getType());
                budgetInfos.add(budgetInfo);
            }
            response.setBudgetInfos(budgetInfos);
            responses.add(response);
        }


        return responses;
    }

    /**
     * 已有预算表单
     */
    public SearchBudgetModel searchBudget(SearchBudgetVo searchBudgetVo) {
        SearchBudgetModel result = new SearchBudgetModel();
        BudgetInfoList budgetInfoList = searchBudgetInfoList(searchBudgetVo.getCorpId(), searchBudgetVo.getDepartId(), null, null, searchBudgetVo.getPageNum(), searchBudgetVo.getPageSize());
        BeanUtils.copyProperties(budgetInfoList, result);
        result.setSeeBudgetList(convertToSearchBudgetDetails(searchBudgetVo.getCorpId(), budgetInfoList.getBudgetInfoList()));
        return result;
    }

    /**
     * 已有预算表单 -- 拼装列表
     */
    private List<SearchBudgetDetailModel> convertToSearchBudgetDetails(String corpId, List<BudgetInfoEntity> budgetInfos) {
        List<SearchBudgetDetailModel> budgetDetails = Lists.newArrayList();
        if (CollectionUtils.isEmpty(budgetInfos)) {
            return budgetDetails;
        }
        BudgetDetailFuture futureResult = searchBudgetDetail(corpId, budgetInfos);
        for (BudgetInfoEntity budgetInfo : budgetInfos) {
            SearchBudgetDetailModel budgetDetail = new SearchBudgetDetailModel();
            budgetDetail.setId(budgetInfo.getBudgetId());
            List<BudgetGroupEntity> groups = futureResult.getMapGroup().get(budgetInfo.getBudgetId());
            if (!CollectionUtils.isEmpty(groups)) {

                if (BudgetScopeEnum.COS.getType().equals(budgetInfo.getBudgetType())) {
                    List<String> rangeNames = Lists.newArrayList();
                    for (BudgetGroupEntity budgetGroup : groups) {
                        CostCenter costCenter = futureResult.getMapCostCenter().get(budgetGroup.getScopeId());
                        if (costCenter != null && CollectionUtils.isNotEmpty(costCenter.getDepartments())) {
                            rangeNames.addAll(costCenter.getDepartments().stream().map(OrgInfo::getName).collect(Collectors.toList()));
                        }
                    }
                    budgetDetail.setBudgetRangeList(rangeNames);
                } else if (BudgetScopeEnum.PRO.getType().equals(budgetInfo.getBudgetType())){
                    List<String> rangeNames = Lists.newArrayList();
                    for (BudgetGroupEntity budgetGroup : groups) {
                        MbOrgProjectInfo projectInfo = futureResult.getMapProject().get(budgetGroup.getScopeId());
                        if (projectInfo != null && CollectionUtils.isNotEmpty(projectInfo.getDepartmentList())) {
                            rangeNames.addAll(projectInfo.getDepartmentList().stream()
                                    .map(projectDepartment -> {
                                        if(StringUtils.isEmpty(projectDepartment.getUserName())) {
                                            return projectDepartment.getOrgName();
                                        } else {
                                            return projectDepartment.getUserName();
                                        }
                                    }).collect(Collectors.toList()));
                        }
                    }
                    budgetDetail.setBudgetRangeList(rangeNames);
                } else {
                    List<String> groupNames = groups.stream().map(BudgetGroupEntity::getScopeName).collect(Collectors.toList());
                    budgetDetail.setBudgetRangeList(groupNames);
                }
                budgetDetail.setBudgetRange(String.join("/", budgetDetail.getBudgetRangeList()));
            }
            budgetDetail.setBudgetTime(DateUtil.dateToString(budgetInfo.getStartTime(), DateUtil.DF_YM) + "-" + DateUtil.dateToString(budgetInfo.getEndTime(), DateUtil.DF_YM));
            List<BudgetItemEntity> items = futureResult.getMapItem().get(budgetInfo.getBudgetId());
            if (!CollectionUtils.isEmpty(items)) {
                boolean isAll = false;
                List<String> typeNames = Lists.newArrayList();
                List<String> hovers = Lists.newArrayList();
                for (BudgetItemEntity item : items) {
                    if (item.getIsAll()) {
                        isAll = true;
                    }
                    typeNames.addAll(Arrays.asList(item.getTypeDesc().split("/")));
                    hovers.add(item.getTypeDesc() + "：¥" + BigDecimalUtil.toPlainString(item.getAmount()));
                }
                budgetDetail.setIfAll(isAll);
                budgetDetail.setBudgetType(isAll ? "全部项" : String.join("、", typeNames));
                budgetDetail.setBudgetTypeList(hovers);
            }
            budgetDetail.setBudgetAmount(budgetInfo.getAmount());
            budgetDetail.setBudgetAlarm(budgetInfo.getWarn());
            List<BudgetNotifyEntity> notifies = futureResult.getMapNotify().get(budgetInfo.getBudgetId());
            List<String> notifyNames = Lists.newArrayList();
            List<String> notifyNameEmails = Lists.newArrayList();
            if (!CollectionUtils.isEmpty(notifies)) {
                for (BudgetNotifyEntity notify : notifies) {
                    notifyNames.add(notify.getName());
                    notifyNameEmails.add(notify.getName() + " " + notify.getEmail());
                }
            }
            budgetDetail.setBeInformed(CollectionUtils.isEmpty(notifyNames) ? "--" : String.join("/", notifyNames));
            budgetDetail.setBudgetPeopleList(notifyNameEmails);
            budgetDetail.setStatus(budgetInfo.getIsActive() ? 1 : 0);
            budgetDetail.setCostCenter(BudgetScopeEnum.COS.getType().equals(budgetInfo.getBudgetType()));
            budgetDetail.setScopeType(budgetInfo.getBudgetType());
            budgetDetails.add(budgetDetail);
        }
        return budgetDetails;
    }

    /**
     * 预算查看
     */
    public SeeBudgetModel seeBudget(SeeBudgetVo seeBudgetVo) {
        SeeBudgetModel result = new SeeBudgetModel();
        BudgetInfoList budgetInfoList = searchBudgetInfoList(seeBudgetVo.getCorpId(), seeBudgetVo.getDepartId(),
                BudgetDateUtil.getFirstDayOfYear(seeBudgetVo.getBudgetTime()), BudgetDateUtil.getLastDayOfYear(seeBudgetVo.getBudgetTime()), seeBudgetVo.getPageNum(), seeBudgetVo.getPageSize());
        BeanUtils.copyProperties(budgetInfoList, result);
        TimeIntervalDTO timeInterval = budgetInfoMapper.selectTimeInterval(seeBudgetVo.getCorpId());
        if (timeInterval != null) {
            result.setBudgetTimeRange(DateUtil.getYearInterval(timeInterval.getStartTime(), timeInterval.getEndTime()));
        }
        if (CollectionUtils.isEmpty(budgetInfoList.getBudgetInfoList())) {
            result.setBudgetTotal("--");
            result.setBudgetSurplus("--");
            result.setSetAmount("--");
            result.setAddTotal("--");
            result.setActualUse("--");
            result.setRoadPreempted("--");
            return result;
        }
        CompletableFuture<Void> totalFuture = CompletableFuture.runAsync(() -> {
            AmountTotalDTO amountTotal = budgetInfoMapper.selectAmounts(seeBudgetVo.getCorpId());
            BigDecimal amountSurplus = BudgetAlarmUtil.calSurplus(amountTotal.getAmount(), amountTotal.getAmountUsed(), amountTotal.getAmountOccupy());
            result.setBudgetTotal(BigDecimalUtil.toTenThousandStr(amountTotal.getAmount()));
            result.setSetAmount(BigDecimalUtil.toTenThousandStr(amountTotal.getAmountInit()));
            result.setActualUse(BigDecimalUtil.toTenThousandStr(amountTotal.getAmountUsed()));
            result.setRoadPreempted(BigDecimalUtil.toTenThousandStr(amountTotal.getAmountOccupy()));
            result.setBudgetSurplus(BigDecimalUtil.toTenThousandStr(amountSurplus));
            BigDecimal recordAmount = budgetInfoMapper.selectAddAmounts(seeBudgetVo.getCorpId());
            result.setAddTotal(BigDecimalUtil.toTenThousandStr(recordAmount));
        }, defaultThreadPoolExecutor);
        result.setSeeBudgetList(convertToSeeBudgetDetails(seeBudgetVo.getCorpId(), budgetInfoList.getBudgetInfoList()));
        try {
            totalFuture.get();
        } catch (Exception ex) {
            log.error("查询预算详情的总计数据失败", ex);
        }
        return result;
    }

    /**
     * 预算查看 -- 拼装列表
     */
    private List<SeeBudgetDetailModel> convertToSeeBudgetDetails(String corpId, List<BudgetInfoEntity> budgetInfos) {
        List<SeeBudgetDetailModel> budgetDetails = Lists.newArrayList();
        BudgetDetailFuture futureResult = searchBudgetDetail(corpId, budgetInfos);
        for (BudgetInfoEntity budgetInfo : budgetInfos) {
            SeeBudgetDetailModel budgetDetail = new SeeBudgetDetailModel();
            budgetDetail.setId(budgetInfo.getBudgetId());
            budgetDetail.setStatus(budgetInfo.getIsActive() ? 1 : 0);
            budgetDetails.add(budgetDetail);
            List<BudgetGroupEntity> groups = futureResult.getMapGroup().get(budgetInfo.getBudgetId());
            if (CollectionUtils.isNotEmpty(groups)) {
                List<String> groupNames = groups.stream().map(BudgetGroupEntity::getScopeName).collect(Collectors.toList());

                if (BudgetScopeEnum.COS.getType().equals(budgetInfo.getBudgetType())) {
                    List<String> rangeNames = Lists.newArrayList();
                    for (BudgetGroupEntity budgetGroup : groups) {
                        CostCenter costCenter = futureResult.getMapCostCenter().get(budgetGroup.getScopeId());
                        if (costCenter != null && !CollectionUtils.isEmpty(costCenter.getDepartments())) {
                            rangeNames.addAll(costCenter.getDepartments().stream().map(OrgInfo::getName).collect(Collectors.toList()));
                        }
                    }
                    budgetDetail.setBudgetRangeList(rangeNames);
                } else if (BudgetScopeEnum.PRO.getType().equals(budgetInfo.getBudgetType())){
                    List<String> rangeNames = Lists.newArrayList();
                    for (BudgetGroupEntity budgetGroup : groups) {
                        MbOrgProjectInfo projectInfo = futureResult.getMapProject().get(budgetGroup.getScopeId());
                        if (projectInfo != null && CollectionUtils.isNotEmpty(projectInfo.getDepartmentList())) {
                            rangeNames.addAll(projectInfo.getDepartmentList().stream()
                                    .map(projectDepartment -> {
                                        if(StringUtils.isEmpty(projectDepartment.getUserName())) {
                                            return projectDepartment.getOrgName();
                                        } else {
                                            return projectDepartment.getUserName();
                                        }
                                    }).collect(Collectors.toList()));
                        }
                    }
                    budgetDetail.setBudgetRangeList(rangeNames);
                } else {
                    budgetDetail.setBudgetRangeList(groupNames);
                }
                budgetDetail.setBudgetRange(String.join("/", budgetDetail.getBudgetRangeList()));
            }
            budgetDetail.setBudgetTime(DateUtil.dateToString(budgetInfo.getStartTime(), DateUtil.DF_YM) + "-" + DateUtil.dateToString(budgetInfo.getEndTime(), DateUtil.DF_YM));
            budgetDetail.setSetTime(DateUtil.dateToString(budgetInfo.getDatachangeCreatetime(), DateUtil.DF_YMD_HM));
            budgetDetail.setSetAmount(budgetInfo.getAmountInit());
            BigDecimal addTotal = new BigDecimal(0);
            List<String> hoverAddtime = Lists.newArrayList();
            List<BudgetRecordEntity> records = futureResult.getMapRecord().get(budgetInfo.getBudgetId());
            if (!CollectionUtils.isEmpty(records)) {
                for (BudgetRecordEntity record : records) {
                    addTotal = addTotal.add(record.getAmount());
                    hoverAddtime.add(DateUtil.dateToString(record.getDatachangeCreatetime(), DateUtil.DF_YMD_HM) + " " + BigDecimalUtil.toTenThousandStr(record.getAmount()));
                }
            }
            budgetDetail.setAddTime(CollectionUtils.isEmpty(records) ? "--" : DateUtil.dateToString(records.get(0).getDatachangeCreatetime(), DateUtil.DF_YMD_HM));
            budgetDetail.setAddTotal(addTotal);
            budgetDetail.setHoverAddtime(hoverAddtime);
            budgetDetail.setBudgetTotal(BudgetAlarmUtil.calTotal(budgetDetail.getSetAmount(), budgetDetail.getAddTotal()));
            budgetDetail.setActualUse(budgetInfo.getAmountUsed());
            budgetDetail.setRoadPreempted(budgetInfo.getAmountOccupy());
            budgetDetail.setBudgetSurplus(BudgetAlarmUtil.calSurplus(budgetDetail.getBudgetTotal(), budgetDetail.getActualUse(), budgetDetail.getRoadPreempted()));
            budgetDetail.setAlarmThreshold(budgetInfo.getWarn());
            budgetDetail.setCostCenter(BudgetScopeEnum.COS.getType().equals(budgetInfo.getBudgetType()));
            budgetDetail.setAlarmStatus(BudgetAlarmUtil.isAlarm(budgetDetail.getBudgetSurplus(), budgetDetail.getBudgetTotal(), budgetDetail.getAlarmThreshold()));
            List<BudgetItemEntity> items = futureResult.getMapItem().get(budgetInfo.getBudgetId());
            if (!CollectionUtils.isEmpty(items)) {
                boolean isAll = false;
                SeeBudgetSubsidiaryModel subsidiary = new SeeBudgetSubsidiaryModel();
                subsidiary.setTime(budgetDetail.getBudgetTime());
                List<SeeBudgetSubsidiaryDetailModel> subsidiaryDetails = Lists.newArrayList();
                for (BudgetItemEntity item : items) {
                    if (item.getIsAll()) {
                        isAll = true;
                    }
                    SeeBudgetSubsidiaryDetailModel subsidiaryDetail = new SeeBudgetSubsidiaryDetailModel();
                    subsidiaryDetail.setType(item.getTypeDesc());
                    subsidiaryDetail.setActualUse(item.getAmountUsed());
                    subsidiaryDetail.setRoadPreempted(item.getAmountOccupy());
                    subsidiaryDetail.setBudgetFee(item.getAmount());
                    subsidiaryDetail.setBudgetAlarm(item.getWarn());
                    subsidiaryDetail.setAlarmStatus(BudgetAlarmUtil.isAlarm(subsidiaryDetail.getBudgetFee(), subsidiaryDetail.getActualUse(), subsidiaryDetail.getRoadPreempted(), subsidiaryDetail.getBudgetAlarm()));
                    //换算单位到万
                    subsidiaryDetail.setActualUse(BigDecimalUtil.toTenThousand(subsidiaryDetail.getActualUse()));
                    subsidiaryDetail.setRoadPreempted(BigDecimalUtil.toTenThousand(subsidiaryDetail.getRoadPreempted()));
                    subsidiaryDetail.setBudgetFee(BigDecimalUtil.toTenThousand(subsidiaryDetail.getBudgetFee()));
                    subsidiaryDetails.add(subsidiaryDetail);
                }
                subsidiary.setLilst(subsidiaryDetails);
                budgetDetail.setOperation(!isAll);
                budgetDetail.setSubsidiary(subsidiary);
            }

            //统一换算单位到万
            budgetDetail.setBudgetTotal(BigDecimalUtil.toTenThousand(budgetDetail.getBudgetTotal()));
            budgetDetail.setBudgetSurplus(BigDecimalUtil.toTenThousand(budgetDetail.getBudgetSurplus()));
            budgetDetail.setSetAmount(BigDecimalUtil.toTenThousand(budgetDetail.getSetAmount()));
            budgetDetail.setAddTotal(BigDecimalUtil.toTenThousand(budgetDetail.getAddTotal()));
            budgetDetail.setActualUse(BigDecimalUtil.toTenThousand(budgetDetail.getActualUse()));
            budgetDetail.setRoadPreempted(BigDecimalUtil.toTenThousand(budgetDetail.getRoadPreempted()));
        }
        return budgetDetails;
    }

    /**
     * 追加数据回显
     */
    public ClickAddModel clickAdd(String budgetId) {
        ClickAddModel result = new ClickAddModel();
        BudgetInfoEntity budgetInfo = budgetInfoMapper.selectByPrimaryKey(budgetId);
        BaseUtils.verifyNotNull(budgetInfo, "没有查询到预算详情");
        BudgetDetailFuture budgetDetailFuture = searchBudgetDetail(StringUtils.EMPTY, Lists.newArrayList(budgetInfo));
        result.setStartTime(DateUtil.dateToString(budgetInfo.getStartTime(), DateUtil.DF_YM));
        result.setEndTime(DateUtil.dateToString(budgetInfo.getEndTime(), DateUtil.DF_YM));
        result.setRangeType(BudgetScopeEnum.getEnumByType(budgetInfo.getBudgetType()).getCode());
        List<BudgetGroupEntity> budgetGroups = budgetDetailFuture.getMapGroup().get(budgetInfo.getBudgetId());
        if (!CollectionUtils.isEmpty(budgetGroups)) {
            result.setDepartment(budgetGroups.stream().map(BudgetGroupEntity::getScopeName).collect(Collectors.joining("/")));
        }
        result.setBudgetAmount(budgetInfo.getAmount());
        result.setAlarmThreshold(budgetInfo.getWarn());
        result.setSendMsg(budgetInfo.getSendMsg());
        List<BudgetNotifyEntity> budgetNotifies = budgetDetailFuture.getMapNotify().get(budgetInfo.getBudgetId());
        List<NotifyScopeModel> notifyScopes = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(budgetNotifies)) {
            notifyScopes = budgetNotifies.stream().map(n -> new NotifyScopeModel(n.getName(), n.getEmail())).collect(Collectors.toList());
        }
        result.setNotifyScope(notifyScopes);
        Set<String> allTypes = Sets.newHashSet();
        List<BudgetTypeModel> budgetTypes = Lists.newArrayList();
        List<BudgetItemEntity> items = budgetDetailFuture.getMapItem().get(budgetInfo.getBudgetId());
        if (!CollectionUtils.isEmpty(items)) {
            for (BudgetItemEntity item : items) {
                BudgetTypeModel budgetType = new BudgetTypeModel();
                budgetType.setId(item.getId());
                String[] types = item.getType().split("/");
                budgetType.setTypes(Arrays.stream(types).map(t -> new TypeDetailModel(t, BudgetTypeEnum.getEnumByType(t).getName())).collect(Collectors.toList()));
                budgetType.setAmount(item.getAmount());
                budgetType.setThreshold(item.getWarn());
                budgetType.setIfAll(item.getIsAll());
                budgetTypes.add(budgetType);
                allTypes.addAll(Lists.newArrayList(types));
            }
        }
        result.setBudgetTypeObject(budgetTypes);
        List<BudgetTypePresetModel> budgetTypePresets = Arrays.stream(BudgetTypeEnum.values()).map(t ->
                new BudgetTypePresetModel(t.getType(), t.getName(), allTypes.contains(t.getType()))).collect(Collectors.toList());
        result.setBudgetTypeList(budgetTypePresets);
        return result;
    }

    public AddBudgetVo getBudgetVo(String budgetId) {
        AddBudgetVo result = new AddBudgetVo();
        BudgetInfoEntity budgetInfo = budgetInfoMapper.selectByPrimaryKey(budgetId);
        BaseUtils.verifyNotNull(budgetInfo, "没有查询到预算详情");
        BudgetDetailFuture budgetDetailFuture = searchBudgetDetail(StringUtils.EMPTY, Lists.newArrayList(budgetInfo));
        result.setStartTime(DateUtil.dateToString(budgetInfo.getStartTime(), DateUtil.DF_YM));
        result.setEndTime(DateUtil.dateToString(budgetInfo.getEndTime(), DateUtil.DF_YM));
        result.setRangeType(BudgetScopeEnum.getEnumByType(budgetInfo.getBudgetType()).getCode());
        List<BudgetGroupEntity> budgetGroups = budgetDetailFuture.getMapGroup().get(budgetInfo.getBudgetId());
        List<DepartmentVo> departmentVos = null;
        if (CollectionUtils.isNotEmpty(budgetGroups)) {
            DepartmentVo departmentVo = null;
            departmentVos = new ArrayList<>();
            for (BudgetGroupEntity budgetGroupEntity : budgetGroups) {
                departmentVo = new DepartmentVo();
                departmentVo.setDepartName(budgetGroupEntity.getScopeName());
                departmentVo.setDepartId(budgetGroupEntity.getScopeId());
                departmentVos.add(departmentVo);
            }
        }
        result.setDepartment(departmentVos);
        result.setBudgetAmount(budgetInfo.getAmount());
        result.setAlarmThreshold(budgetInfo.getWarn());
        result.setSendMsg(budgetInfo.getSendMsg());
        List<BudgetNotifyEntity> budgetNotifies = budgetDetailFuture.getMapNotify().get(budgetInfo.getBudgetId());
        List<NotifyScopeVo> notifyScopes = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(budgetNotifies)) {
            notifyScopes = budgetNotifies.stream().map(n -> new NotifyScopeVo(n.getName(), n.getEmail())).collect(Collectors.toList());
        }
        result.setNotifyScope(notifyScopes);
        Set<String> allTypes = Sets.newHashSet();
        List<BudgetTypeVo> budgetTypes = Lists.newArrayList();
        List<BudgetItemEntity> items = budgetDetailFuture.getMapItem().get(budgetInfo.getBudgetId());
        if (!CollectionUtils.isEmpty(items)) {
            for (BudgetItemEntity item : items) {
                BudgetTypeVo budgetType = new BudgetTypeVo();
                budgetType.setId(item.getId());
                String[] types = item.getType().split("/");
                budgetType.setTypes(Arrays.stream(types).map(t -> new TypeDetailVo(t, BudgetTypeEnum.getEnumByType(t).getName())).collect(Collectors.toList()));
                budgetType.setAmount(item.getAmount());
                budgetType.setThreshold(item.getWarn());
                budgetType.setIfAll(item.getIsAll());
                budgetTypes.add(budgetType);
                allTypes.addAll(Lists.newArrayList(types));
            }
        }
        result.setBudgetTypeObject(budgetTypes);
        result.setStatus(budgetInfo.getIsDeleted() ? BudgetChangeEnum.DELETE.getCode() : (budgetInfo.getIsActive() ? BudgetChangeEnum.ACTIVE.getCode() : BudgetChangeEnum.INACTIVE.getCode()));
        result.setCorpId(budgetInfo.getCorpId());
        return result;
    }

    private List<String> getAccessibleOrgIdList() {
        // 数据权限管控
        Object orgIds = RequestContext.getCurrentContext().getContextParams(DATA_PERMISSION_ORG_ID_LIST);
        List<String> listOrgIds = new ArrayList<>();
        if (Objects.nonNull(orgIds)) {
            String str = JsonUtils.toJsonString(orgIds);
            if (StringUtils.isNotBlank(str)) {
                log.info("获取到的数据权限为：{}", str);
                listOrgIds = JsonUtils.parseArray(str, String.class);
            }
        }
        return listOrgIds;
    }

    /**
     * 预算列表
     */
    private BudgetInfoList searchBudgetInfoList(String corpId, String departId, Date timeFrom, Date timeTo, Integer pageNum, Integer pageSize) {
        BudgetInfoList result = new BudgetInfoList();

        List<String> orgIdList = getAccessibleOrgIdList();

        if (StringUtils.isBlank(departId)&&CollectionUtils.isEmpty(orgIdList)) {
            IPage<BudgetInfoEntity> pageInfo = PageContext.startPage(pageNum, pageSize);
            budgetInfoMapper.selectByCorpIdAndTime(corpId, timeFrom, timeTo);
            result.setBudgetInfoList(pageInfo.getRecords());
            result.setPageNum((int) pageInfo.getCurrent());
            result.setPages((int) pageInfo.getPages());
            result.setPageSize((int) pageInfo.getPages());
            result.setTotal(pageInfo.getTotal());
        } else {
            List<String> orgIds = new ArrayList<>();
            if (StringUtils.isNotBlank(departId) && CollectionUtils.isEmpty(orgIdList)) {
                orgIds.add(departId);
            }
            if (StringUtils.isBlank(departId) && CollectionUtils.isNotEmpty(orgIdList)) {
                orgIds = orgIdList;
            }
            if (StringUtils.isNotBlank(departId) && CollectionUtils.isNotEmpty(orgIdList)) {
                orgIdList.add(departId);
                orgIds = orgIdList.stream().distinct().collect(Collectors.toList());
            }
            IPage<String> pageInfo = PageContext.startPage(pageNum, pageSize);
            budgetGroupMapper.selectBudgetIdByCorpIdAndOrgIdAndTime(corpId, orgIds, timeFrom, timeTo, BudgetScopeEnum.DEP.getType());
            result.setBudgetInfoList(CollectionUtils.isEmpty(pageInfo.getRecords()) ? Lists.newArrayList() : budgetInfoMapper.selectByBudgetIds(pageInfo.getRecords()));
            result.setPageNum((int) pageInfo.getCurrent());
            result.setPages((int) pageInfo.getPages());
            result.setPageSize((int) pageInfo.getPages());
            result.setTotal(pageInfo.getTotal());
        }
        return result;
    }

    /**
     * 查询详情
     */
    private BudgetDetailFuture searchBudgetDetail(String corpId, List<BudgetInfoEntity> budgetInfos) {
        BudgetDetailFuture futureResult = new BudgetDetailFuture();
        List<String> budgetIds = budgetInfos.stream().map(BudgetInfoEntity::getBudgetId).collect(Collectors.toList());
        //查预算范围
        CompletableFuture<Map<String, List<BudgetGroupEntity>>> searchGroupFuture = CompletableFuture.supplyAsync(() -> {
            List<BudgetGroupEntity> budgetGroups = budgetGroupMapper.selectByBudgetIds(budgetIds);
            return budgetGroups.stream().collect(Collectors.groupingBy(BudgetGroupEntity::getBudgetId));
        }, defaultThreadPoolExecutor);
        //查预算项
        CompletableFuture<Map<String, List<BudgetItemEntity>>> searchItemFuture = CompletableFuture.supplyAsync(() -> {
            List<BudgetItemEntity> budgetItems = budgetItemMapper.selectByBudgetIds(budgetIds);
            return budgetItems.stream().collect(Collectors.groupingBy(BudgetItemEntity::getBudgetId));
        }, defaultThreadPoolExecutor);
        //查通知人
        CompletableFuture<Map<String, List<BudgetNotifyEntity>>> searchNotifyFuture = CompletableFuture.supplyAsync(() -> {
            List<BudgetNotifyEntity> budgetNotifies = budgetNotifyMapper.selectByBudgetIds(budgetIds);
            return budgetNotifies.stream().collect(Collectors.groupingBy(BudgetNotifyEntity::getBudgetId));
        }, defaultThreadPoolExecutor);
        //查追加记录
        CompletableFuture<Map<String, List<BudgetRecordEntity>>> searchRecordFuture = CompletableFuture.supplyAsync(() -> {
            List<BudgetRecordEntity> budgetRecords = budgetRecordMapper.selectByBudgetIds(budgetIds);
            return budgetRecords.stream().collect(Collectors.groupingBy(BudgetRecordEntity::getBudgetId));
        }, defaultThreadPoolExecutor);
        //查成本中心
        CompletableFuture<Map<String, CostCenter>> searchCostCenterFuture = CompletableFuture.supplyAsync(() -> {
            boolean containsCosType = false;
            for (BudgetInfoEntity budgetInfo : budgetInfos) {
                if (BudgetScopeEnum.COS.getType().equals(budgetInfo.getBudgetType())) {
                    containsCosType = true;
                    break;
                }
            }
            if (containsCosType && StringUtils.isNotBlank(corpId)) {
                GetProjectInfoRequest getProjectInfoRequest = new GetProjectInfoRequest();
                getProjectInfoRequest.setCompanyId(corpId);
                List<CostCenter> costCenterList = costCenterService.getCostCentersByCompanyId(getProjectInfoRequest);
                if (CollectionUtils.isEmpty(costCenterList)) {
                    log.info(String.format("查询公司[%s]成本中心数据为空", corpId));
                    return Maps.newHashMap();
                }
                return costCenterList.stream().collect(Collectors.toMap(a -> String.valueOf(a.getId()), c -> c, (c1, c2) -> c1));
            } else {
                return Maps.newHashMap();
            }
        }, defaultThreadPoolExecutor);
        //等待所有查询结束
        try {
            CompletableFuture<Void> allFuture = CompletableFuture.allOf(searchGroupFuture, searchItemFuture, searchNotifyFuture, searchRecordFuture, searchCostCenterFuture);
            allFuture.join();
            futureResult.setMapGroup(searchGroupFuture.get());
            futureResult.setMapItem(searchItemFuture.get());
            futureResult.setMapNotify(searchNotifyFuture.get());
            futureResult.setMapRecord(searchRecordFuture.get());
            futureResult.setMapCostCenter(searchCostCenterFuture.get());
        } catch (Exception ex) {
            log.error("查询预算配置详情失败", ex);
            futureResult.check();
        }
        List<Integer> projectIds = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(futureResult.getMapGroup())) {
            Map<String, List<BudgetGroupEntity>> mapGroup = futureResult.getMapGroup();
            mapGroup.forEach((key, value) -> {
                if (value.stream().anyMatch(a -> Objects.equals(a.getScopeType(), BudgetScopeEnum.PRO.getType()))) {
                    projectIds.addAll(value.stream().map(BudgetGroupEntity::getScopeId).map(Integer::valueOf).collect(Collectors.toList()));
                }
            });
        }
        if (CollectionUtils.isNotEmpty(projectIds)) {
            List<MbOrgProjectInfo> mbOrgProjectInfos = iProjectInfoService.selectProjectInfoByCompanyIds(projectIds);
            futureResult.setMapProject(mbOrgProjectInfos.stream().collect(Collectors.toMap(a -> String.valueOf(a.getId()), c -> c, (k1, k2) -> k1)));
        }

        return futureResult;
    }

    /**
     * 查询出行人预算
     */
    public SearchBudgetResponseBO searchBudget(SearchBudgetRequestBO request) {
        SearchBudgetResponseBO response = SearchBudgetResponseBO.success();
        response.setUserBudget(Maps.newHashMap());
        //先将匹配到相同预算配置的出行人合并，再判断该预算配置是否足额
        Map<String, ApplySearchBudgetPojo> budgetPojoMap = mergeToBudgetMap(request);
        budgetPojoMap.keySet().forEach(budgetId -> {
            ApplySearchBudgetPojo applySearchBudgetPojo = budgetPojoMap.get(budgetId);
            BudgetInfoEntity budgetInfo = budgetInfoMapper.selectByPrimaryKey(budgetId);
            List<BudgetGroupEntity> budgetGroups = budgetGroupMapper.selectByBudgetId(budgetId);
            List<BudgetItemEntity> budgetItems = budgetItemMapper.selectByBudgetId(budgetId);
            String budgetDesc = budgetGroups.stream().map(BudgetGroupEntity::getScopeName).collect(Collectors.joining("/"));
            String threshold = BigDecimalUtil.toPlainString(budgetInfo.getWarn()) + "%";
            //各预算项查询结果
            List<ProductBudgetModel> productBudgets = Lists.newArrayList();
            //判断各产线预算状态
            Set<String> productSet = applySearchBudgetPojo.getProductFee().keySet();
            Set<String> productAll = Sets.newHashSet();
            for (BudgetItemEntity budgetItem : budgetItems) {
                Set<String> products = Sets.newHashSet();
                BigDecimal fee = new BigDecimal(0);
                for (String product : budgetItem.getType().split("/")) {
                    productAll.add(product);
                    if (productSet.contains(product)) {
                        products.add(product);
                        fee = fee.add(applySearchBudgetPojo.getProductFee().get(product));
                    }
                }
                if (!products.isEmpty()) {
                    BigDecimal surplus = BudgetAlarmUtil.calSurplus(budgetItem.getAmount(), budgetItem.getAmountUsed(), budgetItem.getAmountOccupy());
                    ProductBudgetModel productBudgetModel = new ProductBudgetModel(budgetItem.getTypeDesc(), BigDecimalUtil.toPlainString(budgetItem.getWarn()) + "%");
                    productBudgetModel.setBudgetStatus(getBudgetStatus(budgetInfo.getIsActive(), fee, surplus, budgetItem.getAmount(), budgetItem.getWarn()).getStatus());
                    productBudgetModel.setProducts(products);
                    productBudgets.add(productBudgetModel);
                }
            }
            //不受管控的产线类型，求差集
            Set<String> productsNotControlled = SetUtils.difference(productSet, productAll);
            //判断总预算状态
            BigDecimal surplusTotal = BudgetAlarmUtil.calSurplus(budgetInfo.getAmount(), budgetInfo.getAmountUsed(), budgetInfo.getAmountOccupy());
            BigDecimal feeTotal = getFeeTotal(applySearchBudgetPojo.getProductFee(), productsNotControlled);
            String budgetStatus = getBudgetStatus(budgetInfo.getIsActive(), feeTotal, surplusTotal, budgetInfo.getAmount(), budgetInfo.getWarn()).getStatus();
            //是否是全配置项
            Optional<BudgetItemEntity> any = budgetItems.stream().filter(BudgetItemEntity::getIsAll).findAny();
            applySearchBudgetPojo.getApplyPersons().forEach(person -> {
                ApplySearchBudgetModel applySearchBudgetModel = new ApplySearchBudgetModel(person.getUid(), person.getUname(), budgetId,
                        budgetDesc, threshold, any.isPresent());
                applySearchBudgetModel.setBudgetStatus(budgetStatus);
                applySearchBudgetModel.setProductsNotControlled(productsNotControlled);
                applySearchBudgetModel.setProductBudgets(productBudgets);
                response.getUserBudget().put(person.getUid(), applySearchBudgetModel);
            });
        });
        return response;
    }

    /**
     * 合并匹配到相同预算配置的出行人
     */
    private Map<String, ApplySearchBudgetPojo> mergeToBudgetMap(SearchBudgetRequestBO request) {
        Map<String, ApplySearchBudgetPojo> result = Maps.newHashMap();
        request.getApplySearchBudgetVos().forEach(t -> {
            List<String> budgetIds = searchBudgetId(t.getCorpId(), t.getDepId(), t.getCosCode(), request.getStartTime(), request.getEndTime());
            //查不到预算配置时不管控
            if (!CollectionUtils.isEmpty(budgetIds)) {
                PreCheckUtil.checkArgument(budgetIds.size() == 1, "查询预算配置失败，匹配到多个");
                String budgetId = budgetIds.get(0);
                if (result.containsKey(budgetId)) {
                    ApplySearchBudgetPojo applySearchBudgetPojo = result.get(budgetId);
                    applySearchBudgetPojo.getApplyPersons().add(new ApplyPersonPojo(t.getUid(), t.getUname()));
                    Map<String, BigDecimal> productFee = applySearchBudgetPojo.getProductFee();
                    t.getProductFee().keySet().forEach(type -> {
                        BigDecimal value = t.getProductFee().get(type);
                        if (productFee.containsKey(type)) {
                            productFee.put(type, productFee.get(type).add(value));
                        } else {
                            productFee.put(type, value);
                        }
                    });
                } else {
                    ApplySearchBudgetPojo applySearchBudgetPojo = new ApplySearchBudgetPojo();
                    applySearchBudgetPojo.setApplyPersons(Lists.newArrayList(new ApplyPersonPojo(t.getUid(), t.getUname())));
                    applySearchBudgetPojo.setProductFee(Maps.newHashMap(t.getProductFee()));
                    result.put(budgetId, applySearchBudgetPojo);
                }
            } else {
                log.info(String.format("[%s][%s][%s][%s][%s][%s]没有查询到预算配置，不管控", t.getUid(), t.getCorpId(), t.getDepId(), t.getCosCode(), request.getStartTime(), request.getEndTime()));
            }
        });
        return result;
    }

    /**
     * 查询出行人预算 -- 配置单Id
     */
    private List<String> searchBudgetId(String corpId, String depId, String cosCode, String startTimeStr, String endTimeStr) {
        Date startTime = DateUtil.stringToDate(startTimeStr, DateUtil.DF_YMD);
        Date endTime = DateUtil.stringToDate(endTimeStr, DateUtil.DF_YMD);
        List<String> budgetIds = budgetGroupMapper.matchApplyTripByStartTime(corpId, depId, startTime, endTime);
        //这里留意成本中心是可以为空的
        return !CollectionUtils.isEmpty(budgetIds) ? budgetIds :
                StringUtils.isNotBlank(cosCode) ? budgetGroupMapper.matchApplyTripByStartTime(corpId, cosCode, startTime, endTime) : null;
    }

    /**
     * 获取出行人金额总和，需要排除不受管控的产线
     */
    private BigDecimal getFeeTotal(Map<String, BigDecimal> productFee, Set<String> productsNotControlled) {
        BigDecimal result = new BigDecimal(0);
        for (String product : productFee.keySet()) {
            if (!productsNotControlled.contains(product)) {
                result = result.add(productFee.get(product));
            }
        }
        return result;
    }

    /**
     * 判断预算状态
     */
    private BudgetStatusEnum getBudgetStatus(boolean isActive, BigDecimal fee, BigDecimal surplus, BigDecimal amount, BigDecimal warn) {
        if (!isActive) {
            return BudgetStatusEnum.FROZEN;
        }
        if (BudgetAlarmUtil.isOverBudget(fee, surplus)) {
            return BudgetStatusEnum.NOT_ENOUGH;
        }
        if (BudgetAlarmUtil.isAlarm(surplus.subtract(fee), amount, warn)) {
            return BudgetStatusEnum.LESS_THRESHOLD;
        }
        return BudgetStatusEnum.NORMAL;
    }
}